#pragma once


#include "Business/IMarketHandler.h"
#include "Business/Market.h"
#include <boost/circular_buffer.hpp>

#include "Utils/Logger.h"

class KLineItem
{
public:
	KLineItem(double lastQuantity, double lastPrice,double minPrice, double maxPrice)
		:m_lastQuantity(lastQuantity),m_lastPrice(lastPrice),m_minPrice(minPrice),m_maxPrice(maxPrice)
	{
	}
	double GetLastQuantity() const { return m_lastQuantity; }
	double GetLastPrice() const { return m_lastPrice; }
	double GetMinPrice() const { return m_minPrice; }
	double GetMaxPrice() const { return m_maxPrice; }
private:
	double m_lastQuantity;
	double m_lastPrice;
	double m_minPrice;
	double m_maxPrice;
};

class KLine
{
public:
	KLine(size_t minutes, size_t maxCount)
		:m_minuteMarketChecker(minutes),m_maxCount(maxCount),m_KLines(maxCount)
	{
		InitMinMax();
	}
	bool OnMarket(Market const& market)
	{
		UpdateMinMax(market);

		if (m_minuteMarketChecker.IsNewMarket(market))
		{
			LightLogger::Info(FT("New KLine: [%s] market time[%s], last open interest[%.2f], last price[%.2f], min price[%.2f],max price[%.2f]",
				market.GetCode(),market.GetTime().Format("%H:%M:%S"),market.GetOpenInterest(),market.GetLastPrice(),m_minPrice,m_maxPrice));
			KLineItem item(market.GetOpenInterest(),market.GetLastPrice(),m_minPrice,m_maxPrice);
			m_KLines.push_back(item);
			InitMinMax();
			return true;
		}
		return false;
	}

	double RefClose(int n) const
	{
		return Ref(n).GetLastPrice();
	}
	double RefOpenint(int n) const
	{
		return Ref(n).GetLastQuantity();
	}
	size_t GetSize() const 
	{
		return m_KLines.size();
	}
	KLineItem const& Ref(int n) const 
	{
		return *(m_KLines.rbegin() + n);
	}
private:

	void InitMinMax() 
	{
		m_maxPrice = 0;
		m_minPrice = 1000000000;
	}
	void UpdateMinMax(Market const &market) 
	{
		if (m_maxPrice < market.GetLastPrice())
		{
			m_maxPrice = market.GetLastPrice();
		}
		if (m_minPrice > market.GetLastPrice())
		{
			m_minPrice = market.GetLastPrice();
		}
	}

	class MinuteMarketChecker
	{
		static const int MINUTE_SECOND = 60;
	public:
		MinuteMarketChecker(size_t minute)
			:m_second(minute * MINUTE_SECOND),m_lastMarketTime(2000,1,1,0,0,0)
		{
		}
		bool IsNewMarket(Market const& market) const
		{
			if (m_lastMarketTime.GetYear() == 2000)
			{
				CTime marketTime(market.GetTime().GetYear(),market.GetTime().GetMonth(),market.GetTime().GetDay(),
					market.GetTime().GetHour(),market.GetTime().GetMinute(),0) ;
				m_lastMarketTime = marketTime;
				return false;
			}
			CTimeSpan timespan = market.GetTime() - m_lastMarketTime;
			if (timespan.GetTotalSeconds() >= m_second) 
			{
				CTime marketTime(market.GetTime().GetYear(),market.GetTime().GetMonth(),market.GetTime().GetDay(),
					market.GetTime().GetHour(),market.GetTime().GetMinute(),0) ;
				m_lastMarketTime = marketTime;
				return true;
			} 
			return false;
		}
	private:
		size_t m_second;
		mutable CTime m_lastMarketTime;
	};
	MinuteMarketChecker m_minuteMarketChecker;

	size_t m_maxCount;
	boost::circular_buffer<KLineItem> m_KLines;

	double m_minPrice;
	double m_maxPrice;
};